﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NFit.Core.Errors;
using NFit.Core.BaseClasses;

namespace NFit.Core
{
    /// <summary>
    /// Definition of a specification
    /// </summary>
    public class SpecificationDefinition : RunnableActiveList<FixtureDefinition, AbstractSpecImplementation>
    {
        /// <summary>
        /// Fixtures to run for spec setup
        /// </summary>
        public SpecificationDefinition Setup { get; set; }

        /// <summary>
        /// Fixtures to run for spec teardown
        /// </summary>
        public SpecificationDefinition Teardown { get; set; }

        /// <summary>
        /// The fixtures of the specification
        /// </summary>
        public IList<FixtureDefinition> Fixtures
        {
            get { return ChildElements; }
        }

        /// <summary>
        /// The parent specification
        /// </summary>
        public SpecificationDefinition ParentSpecification { get; set; }

        private IList<SpecificationDefinition> _childSpecs = new List<SpecificationDefinition>();
        /// <summary>
        /// Childs specifications
        /// </summary>
        public IList<SpecificationDefinition> ChildSpecs
        {
            get { return _childSpecs; }
            set { _childSpecs = value; }
        }

        private Dictionary<string, object> _ht;
        /// <summary>
        /// The execution params
        /// </summary>
        public Dictionary<string, object> ExecutionParams
        {
            get
            {
                if (_ht == null)
                    _ht = new Dictionary<string, object>();

                return _ht;
            }
        }    
    
        /// <summary>
        /// Add a spec to the childs list
        /// </summary>
        /// <param name="spec"></param>
        public void AddChildSpec(SpecificationDefinition spec)
        {
            spec.ParentSpecification = this;
            ChildSpecs.Add(spec);
        }

        /// <summary>
        /// Add a fixture
        /// </summary>
        /// <param name="act"></param>
        /// <returns></returns>
        public SpecificationDefinition AddFixture(Action<FixtureDefinition> act)
        {
            FixtureDefinition fixture = new FixtureDefinition();
            act(fixture);
            return this;
        }

        /// <summary>
        /// Add a fixture
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="act"></param>
        /// <returns></returns>
        public SpecificationDefinition AddFixture<T>(Action<FixtureDefinition> act) where T:AbstractFixture
        {
            FixtureDefinition fixture = new FixtureDefinition<T>();
            act(fixture);
            return this;
        }

        /// <summary>
        /// Add a fixture
        /// </summary>
        /// <returns></returns>
        public FixtureDefinition AddFixture()
        {
            FixtureDefinition def = new FixtureDefinition();
            Fixtures.Add(def);
            return def;
        }

        /// <summary>
        /// Add a fixture
        /// </summary>
        /// <param name="ctorArgs"></param>
        /// <returns></returns>
        public FixtureDefinition AddFixture(params string[] ctorArgs)
        {
            FixtureDefinition def = new FixtureDefinition();
            def.Arguments = new FixtureRow();
            foreach (string s in ctorArgs)
                def.Arguments.Add(new FixtureCell{Text = s});
            Fixtures.Add(def);
            return def;
        }

        /// <summary>
        /// Add a fixture
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public FixtureDefinition AddFixture<T>() where T:AbstractFixture
        {
            FixtureDefinition<T> def = new FixtureDefinition<T>();
            Fixtures.Add(def);
            return def;
        }

        /// <summary>
        /// Add a fixture
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ctorArgs"></param>
        /// <returns></returns>
        public FixtureDefinition AddFixture<T>(params string[] ctorArgs) where T : AbstractFixture
        {
            FixtureDefinition<T> def = new FixtureDefinition<T>();
            def.Arguments = new FixtureRow();
            foreach (string s in ctorArgs)
                def.Arguments.Add(new FixtureCell { Text = s });
            Fixtures.Add(def);
            return def;
        }

        /// <summary>
        /// Initialize the specification
        /// </summary>
        public virtual void Initialize()
        {
            
        }
    }

    /// <summary>
    /// Definition of a specification
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SpecificationDefinition<T> : SpecificationDefinition where T : AbstractSpecImplementation
    {
        /// <summary>
        /// Constructor
        /// </summary>
        public SpecificationDefinition()
        {
            ClassType = typeof(T);
        }
    }
}
